﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM
{
    #region Using Statements

    using System.Linq;
    using System.Collections.Generic;

    #endregion

    public class NamedNodeMap
    {
        #region Properties

        internal List<Node> Nodes { get; set; }

        #endregion

        #region Constructors

        public NamedNodeMap()
        {
            Nodes = new List<Node>();
        }

        public NamedNodeMap(List<Node> _nodes)
        {
            Nodes = _nodes;
        }

        #endregion

        #region DOM Level 1 Compliance

        /// <summary>
        /// Retrieves a node specified by name. 
        /// </summary>
        /// <param name="name">Name of a node to retrieve.</param>
        /// <returns>A Node (of any type) with the specified name, or null if the specified name did not identify any node in the map.</returns>
        public Node getNamedItem(string name)
        {
            var result = Nodes.Where(n => n.nodeName.Equals(name));

            return result.Count() > 0 ? result.First() : null;
        }

        /// <summary>
        /// Returns the indexth item in the map. If index is greater than or equal to the number of nodes in the map, this returns null. 
        /// </summary>
        /// <param name="index">Index into the map.</param>
        /// <returns>The node at the indexth position in the NamedNodeMap, or null if that is not a valid index.</returns>
        public Node item(int index)
        {
            return (index > -1 && index < Nodes.Count ? Nodes[index] : null);
        }

        /// <summary>
        /// The number of nodes in the map. The range of valid child node indices is 0 to length-1 inclusive. 
        /// </summary>
        public int length { get { return Nodes.Count; } }

        /// <summary>
        /// Removes a node specified by name. If the removed node is an Attr with a default value it is immediately replaced. 
        /// </summary>
        /// <param name="nodeName">The name of a node to remove.</param>
        /// <returns>The node removed from the map or null if no node with such a name exists.</returns>
        public Node removeNamedItem(string nodeName)
        {
            var node = getNamedItem(nodeName);

            if (node != null)
            {
                Nodes.Remove(node);
                return node;
            }
            return null;
        }

        /// <summary>
        /// Adds a node using its nodeName attribute.
        /// 
        /// As the nodeName attribute is used to derive the name which the
        /// node must be stored under, multiple nodes of certain types (those
        /// that have a "special" string value) cannot be stored as the names
        /// would clash. This is seen as preferable to allowing nodes 
        /// to be aliased. 
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public Node setNamedItem(Node node)
        {
            var oldNode = getNamedItem(node.nodeName);

            if (oldNode != null)
            {
                Nodes[Nodes.IndexOf(oldNode)] = node;
                return oldNode;
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region Non-Standard Helper Methods

        /// <summary>
        /// Determines if two NamedNodeMaps are the same (for isSameNode()).
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            NamedNodeMap other = obj as NamedNodeMap;

            if (other != null)
            {
                if (other.length != length) return false;

                for (int i = 0; i < Nodes.Count; i++)
                {
                    Node n1 = Nodes[i];
                    Node n2 = other.Nodes[i];

                    if (!n1.isSameNode(n2)) return false;
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion
    }
}
